<template>
  <div :class="{ seleArea: true, 'is-disabled': disabled }">
    <div class="seleArea-province">
      <div class="scrollbar">
        <div ref="provinceRef" class="scrollbar-wrap">
          <ul class="scrollbar-view">
            <li
              v-for="{ value, id } in data"
              :key="id"
              :class="{
                'seleArea-item': true,
                'is-active': area.province === id,
                'is-link': area.province !== id,
              }"
              @click="selectArea(0, id)"
            >
              {{ value }}
            </li>
          </ul>
        </div>
      </div>
    </div>
    <div class="seleArea-city">
      <div class="scrollbar">
        <div ref="cityRef" class="scrollbar-wrap">
          <ul class="scrollbar-view">
            <li
              v-for="{ value, id } in cityDatas"
              :key="id"
              :class="{
                'seleArea-item': true,
                'is-active': area.city === id,
                'is-link': area.city !== id,
              }"
              @click="selectArea(1, id)"
            >
              {{ value }}
            </li>
          </ul>
        </div>
      </div>
    </div>
    <div v-if="disabled" class="select-disabled"></div>
  </div>
</template>

<script lang="ts" setup>
import { ref, reactive, onMounted, watch, onBeforeUnmount } from "vue";
import type { Area } from "@/staticDatas/interface";

const props = withDefaults(
  defineProps<{
    modelValue: [number, number];
    data: Array<Area>;
    disabled?: boolean;
  }>(),
  {
    disabled: false,
  }
);

const emits = defineEmits<{
  (e: "update:modelValue", areaCoord: [number, number]): void;
}>();

watch(
  () => props.modelValue,
  (newValue) => {
    const isUpdate: boolean = setArea(newValue);
    if (isUpdate) {
      removeScrollEvent();
      setTimeout(() => {
        provinceRef.value.scrollTop = 40 * area.province;
        cityRef.value.scrollTop = 40 * area.city;
        setTimeout(() => addScrollEvent(), 50);
      });
    }
  }
);

/**
 * 当前用户所选的省区
 */
const area = reactive<{
  province: number;
  city: number;
}>({
  province: 0,
  city: 0,
});

/**
 * 城市数据
 */
const cityDatas = reactive<Array<Omit<Area, "children">>>([...props.data[0].children]);

/**
 * 将父组件的最新modelValue值赋值给area
 * @return area是否更新
 */
const setArea = (areaCoord: [number, number]): boolean => {
  let boo: boolean = false;
  if (areaCoord[0] === area.province && areaCoord[1] === area.city) return boo;
  // 省份数最大索引值
  const provinceIndex: number = props.data.length - 1;
  // 如果传来的区域坐标索引的省份索引在该区间内，且城市索引也需要在对应的索引内
  if (areaCoord[0] >= 0 && areaCoord[0] <= provinceIndex) {
    // 该省份下的城市的最大索引值
    const cityIndex: number = props.data[areaCoord[0]].children.length - 1;
    if (areaCoord[1] >= 0 && areaCoord[1] <= cityIndex) {
      area.province = areaCoord[0];
      area.city = areaCoord[1];
      cityDatas.splice(0);
      cityDatas.unshift(...props.data[area.province].children);
      boo = true;
    }
  }
  return boo;
};

/**
 * 给省份栏元素一个ref属性
 */
const provinceRef = ref<HTMLDivElement>(document.createElement("div"));
/**
 * 给城市栏元素一个ref属性
 */
const cityRef = ref<HTMLDivElement>(document.createElement("div"));

/**
 * 省份栏开始滚动
 */
const provinceScroll = (e: Event) => scroll(0, e);
/**
 * 城市栏开始滚动
 */
const cityScroll = (e: Event) => scroll(1, e);

/**
 * 给省份、城市栏添加滚动事件
 */
const addScrollEvent = () => {
  provinceRef.value.addEventListener("scroll", provinceScroll);
  cityRef.value.addEventListener("scroll", cityScroll);
};
/**
 * 给省份、城市栏移除滚动事件
 */
const removeScrollEvent = () => {
  provinceRef.value.removeEventListener("scroll", provinceScroll);
  cityRef.value.removeEventListener("scroll", cityScroll);
};

// 省份栏或城市栏滚动时触发的动作
const scroll = (() => {
  let timingProvince: number = 0,
    timingCity: number = 0;
  return (type: 0 | 1, e: Event) => {
    const ele: HTMLDivElement = e!.target as HTMLDivElement;
    const top: number = ele.scrollTop;
    // 滚动的格子数
    const count: number = Math.floor(top / 40);
    switch (type) {
      case 0:
        if (timingProvince) clearTimeout(timingProvince);
        area.province = props.data[count].id;
        timingProvince = window.setTimeout(() => {
          provinceRef.value.removeEventListener("scroll", provinceScroll);
          provinceRef.value.scrollTop = 40 * area.province;
          setTimeout(() => provinceRef.value.addEventListener("scroll", provinceScroll));
          cityDatas.splice(0);
          cityDatas.unshift(...props.data[area.province].children);
          if (area.city !== 0) {
            cityRef.value.removeEventListener("scroll", cityScroll);
            area.city = 0;
            cityRef.value.scrollTop = 0;
            setTimeout(() => cityRef.value.addEventListener("scroll", cityScroll));
          }
        }, 200);
        break;
      case 1:
        if (timingCity) clearTimeout(timingCity);
        area.city = props.data[area.province].children[count].id;
        timingCity = window.setTimeout(() => {
          cityRef.value.removeEventListener("scroll", cityScroll);
          cityRef.value.scrollTop = 40 * area.city;
          setTimeout(() => cityRef.value.addEventListener("scroll", cityScroll));
        }, 200);
        break;
    }
    emits("update:modelValue", [area.province, area.city]);
  };
})();

/**
 * 点击省份或者城市进行选择
 * @param type - 是点击省份还是城市，0代表省份
 * @param id - 当前被点击省份或者城市的id值
 */
const selectArea = (type: 0 | 1, id: number) => {
  switch (type) {
    case 0:
      if (id !== area.province) {
        provinceRef.value.removeEventListener("scroll", provinceScroll);
        area.province = id;
        provinceRef.value.scrollTop = 40 * area.province;
        setTimeout(() => provinceRef.value.addEventListener("scroll", provinceScroll));
        cityDatas.splice(0);
        cityDatas.unshift(...props.data[area.province].children);
        if (area.city !== 0) {
          cityRef.value.removeEventListener("scroll", cityScroll);
          area.city = 0;
          cityRef.value.scrollTop = 0;
          setTimeout(() => cityRef.value.addEventListener("scroll", cityScroll));
        }
        emits("update:modelValue", [area.province, area.city]);
      }
      break;
    case 1:
      if (id !== area.city) {
        cityRef.value.removeEventListener("scroll", cityScroll);
        area.city = id;
        cityRef.value.scrollTop = 40 * area.city;
        emits("update:modelValue", [area.province, area.city]);
        setTimeout(() => cityRef.value.addEventListener("scroll", cityScroll));
      }
      break;
  }
};

onMounted(() => {
  const boo: boolean = setArea(props.modelValue);
  if (boo) {
    setTimeout(() => {
      provinceRef.value.scrollTop = 40 * area.province;
      cityRef.value.scrollTop = 40 * area.city;
      setTimeout(() => addScrollEvent());
    });
  } else addScrollEvent();
});

onBeforeUnmount(() => removeScrollEvent());
</script>

<style lang="scss" scoped>
.seleArea {
  position: relative;
  overflow: hidden;

  @mixin thame() {
    content: "";
    position: absolute;
    background-color: #dbdde3;
    height: 0.5px;
    left: calc(100% / 4 - 20px);
    right: calc(100% / 4 - 20px);
    box-sizing: border-box;
    pointer-events: none;
  }

  &::before {
    @include thame();
    top: 50%;
    margin-top: -20px;
  }

  &::after {
    @include thame();
    bottom: 50%;
    margin-bottom: -20px;
  }

  &-province {
    width: 50%;
  }

  &-item {
    height: 40px;
    line-height: 40px;
    font-size: 13px;
    text-align: center;
  }

  &-city {
    width: 50%;
    position: absolute;
    top: 0;
    left: 50%;
  }
}

.scrollbar {
  height: 200px;
  overflow: hidden;

  &-wrap {
    height: 100%;
    overflow-y: auto;
    scrollbar-width: none; /* firefox */
    -ms-overflow-style: none; /* IE 10+ */
    &::-webkit-scrollbar {
      display: none;
    }
  }

  &-view {
    &::before,
    &::after {
      content: "";
      display: block;
      width: 100%;
      height: 80px;
      box-sizing: border-box;
    }
  }
}

.is {
  &-active {
    font-weight: 700;
  }

  &-link {
    color: #6e7074;
    cursor: pointer;
  }

  &-disabled {
    color: #989ba0 !important;
  }
}

.select-disabled {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 100;
  cursor: not-allowed;
}
</style>
